//<?php


/*
 * Created on Sep 12, 2006
 *
 * To change the template for this generated file go to
 * Window - Preferences - PHPeclipse - PHP - Code Templates
 */
//error_reporting(E_ALL);

// Gets urls
class linkChecker {

	var $linkCache = array ();

	function getUrls($html, $url) {
		global $modx;
		
		// Gets url ready to use
		$info = @ parse_url($url);

		// Splits the headers from the html
		$pieces = explode("\r\n\r\n", $html, 2);
		$html = $pieces[1];
		unset ($pieces);

		// Finds any links in the HTML
		$matches = array ();
		preg_match_all("|href\=\"?'?`?([[:alnum:]:?=&@/;._-]+)\"?'?`?|i", $html, & $matches);
		$links = array ();
		$ret = $matches[1];

		for ($i = 0; $i < count($ret); $i++) {
			if (preg_match("/^(http|https|ftp):\/\/((?:[a-zA-Z0-9_-]+.?)+):?(d*)/i", $ret[$i])) {
				$links[] = $ret[$i];
			}
			elseif (preg_match("|^/(.*)|i", $ret[$i])) {
				$links[] = "http://" . $info["host"] . $modx->config['base_url'] . ltrim($ret[$i],'/');
			}
			elseif (preg_match("|^(.*)|i", $ret[$i]) && !preg_match("|^javascript:.*|i", $ret[$i])) {
				$links[] = "http://" . $info["host"] . $modx->config['base_url'] . $ret[$i];
			}
		}
		return $links;
	}
	
	function getInternalRef($url) {
		
	}

	// Gets Unique Urls
	function setUniqueUrls($html, $url) {
		$uniqueUrls = array ();
		if (!$html) {
			return false;
		}
		// Gets the list of urls
		$urls = $this->getUrls($html, $url);
		for ($i = 0; isset ($urls[$i]); $i++) {
			// Checks if the url is in the array
			if (!array_key_exists($urls[$i], $this->linkCache)) {
				// If it's not it adds it
				$this->linkCache[$urls[$i]] = 0;
				$uniqueUrls[] = $urls[$i];
			}
		}
		return $urls;
	}

	// Gets everything headers and HTML
	function getEverything($url) {
		// Gets url ready to use
		$info = @ parse_url($url);

		// Opens socket
		$fp = @ fsockopen($info["host"], 80, $errno, $errstr, 10);

		// Makes sure the socket is open or returns false
		if (!$fp) {
			return false;
		} else {

			// Checks the path is not empty
			if (empty ($info["path"])) {

				// If it is empty it fills it
				$info["path"] = "/";
			}
			$query = "";

			// Checks if there is a query string in the url
			if (isset ($info["query"])) {

				// If there is a query string it adds a ? to the front of it
				$query = "?" . $info["query"] . "";
			}

			// Sets the headers to send
			$out = "GET " . $info["path"] . "" . $query . " HTTP/1.0\r\n";
			$out .= "Host: " . $info["host"] . "\r\n";
			$out .= "Connection: close \r\n";
			$out .= "User-Agent: link_checker/1.1\r\n\r\n";

			// writes the headers out
			fwrite($fp, $out);
			$html = '';

			// Reads what gets sent back
			while (!feof($fp)) {
				$html .= fread($fp, 8192);
			}

			// Closes socket
			fclose($fp);
		}
		return $html;
	}

	// Gets everything headers
	function getHead($url) {
		// Gets url ready to use
		$info = @ parse_url($url);

		// Opens socket
		$fp = @ fsockopen($info["host"], 80, $errno, $errstr, 10);

		// Makes sure the socket is open or returns false
		if (!$fp) {
			return false;
		} else {

			// Checks the path is not empty
			if (empty ($info["path"])) {

				// If it is empty it fills it
				$info["path"] = "/";
			}
			$query = "";

			// Checks if there is a query string in the url
			if (isset ($info["query"])) {

				// If there is a query string it adds a ? to the front of it
				$query = "?" . $info["query"] . "";
			}

			// Sets the headers to send
			$out = "HEAD " . $info["path"] . "" . $query . " HTTP/1.0\r\n";
			$out .= "Host: " . $info["host"] . "\r\n";
			$out .= "Connection: close \r\n";
			$out .= "User-Agent: link_checker/1.1\r\n\r\n";

			// writes the headers out
			fwrite($fp, $out);
			$html = '';

			// Reads what gets sent back
			while (!feof($fp)) {
				$html .= fread($fp, 8192);
			}

			// Closes socket
			fclose($fp);
		}
		return $html;
	}

	// Gets status code
	function getStatusCode($header, $url) {

		// Splits the headers into an array
		$headers = explode("\r\n", $header);
		unset ($header);
		for ($i = 0; isset ($headers[$i]); $i++) {

			// Checks if the header is the status header
			if (preg_match("/HTTP\/[0-9A-Za-z +]/i", $headers[$i])) {

				// If it is save the status
				$status = preg_replace("/http\/[0-9]\.[0-9]/i", "", $headers[$i]);
			}
		}
		if (array_key_exists($url, $this->linkCache))
			$this->linkCache[$url] = $status;
		return $status;
	}

	/**
	* Process Range
	* 
	* @pids - The document ID range
	* @column - The column to return (eg. id,document etc.) if returnval is 1.
	* @returnval - Return values as array(0) or SQL WHERE string (1)
	*/
	function processRange($pids, $column, $returnval = 1) {
		global $modx;

		$table = $modx->getFullTableName('site_content');

		//-- set initial vars 
		$values = array ();
		$error = '';

		//-- check for empty field 
		if (trim($pids) <> '') {
			$values = explode(',', $pids);
		} else {
			$error .= 'No values to process.';
		}

		$pids = '';

		//-- parse values, and check for invalid entries 
		foreach ($values as $key => $value) {
			//-- value is a range 
			if (preg_match('/^[\d]+\-[\d]+$/', trim($value))) {
				//-- explode the lower and upper limits 
				$match = explode('-', $value);
				//-- Upper limit lower than lower limit 
				if (($match[1] - $match[0]) < 0) {
					$error = 'Lower limit higher than Upper limit: ' . $value . '<br />';
				}
				//-- loop through values and parse WHERE SQL statement 
				$loop = $match[1] - $match[0];
				for ($i = 0; $i <= $loop; $i++) {
					if (returnval == 0) {
						$idarray[] = ($i + $match[0]);
					} else {
						$pids .= '' . $column . '=\'' . ($i + $match[0]) . '\' OR ';
					}
				}
			}

			//-- value is a group for immediate children 
			elseif (preg_match('/^[\d]+\*$/', trim($value), $match)) {
				//-- get ID number of folder 
				$match = rtrim($match[0], '*');
				//-- get ALL children 
				$group = $modx->db->select('id', $table, 'parent=' . $match, '', '');
				//-- parse WHERE SQL statement 
				if ($returnval == 0) {
					$idarray[] = $match;
				} else {
					$pids .= '' . $column . '=\'' . $match . '\' OR ';
				}
				while ($row = $modx->db->getRow($group)) {
					if (returnval == 0) {
						$idarray[] = ($row['id']);
					} else {
						$pids .= '' . $column . '=\'' . $row['id'] . '\' OR ';
					}
				}
			}
			//-- value is a group for ALL children 
			elseif (preg_match('/^[\d]+\*\*$/', trim($value), $match)) {
				//-- get ID number of folder 
				$match = rtrim($match[0], '**');
				$idarray[] = $match;
				//-- recurse and get ALL children 
				for ($i = 0; $i < count($idarray); $i++) {
					$where = 'parent=' . $idarray[$i];
					$rs = $modx->db->select("id", $table, $where);
					if ($modx->db->getRecordCount($rs) > 0) {
						while ($row = $modx->db->getRow($rs)) {
							$idarray[] = $row['id'];
						}
					}
				}

				//-- parse array into string 
				for ($i = 0; $i < count($idarray); $i++) {
					$pids .= '' . $column . '=\'' . $idarray[$i] . '\' OR ';
				}
			}
			//-- value is a single document 
			elseif (preg_match('/^[\d]+$/', trim($value), $match)) {
				//-- parse WHERE SQL statement 
				if ($returnval == 0) {
					$idarray[] = ($i + $match[0]);
				} else {
					$pids .= '' . $column . '=\'' . trim($value) . '\' OR ';
				}
				//-- value is invalid 
			} else {
				$error .= 'Invalid Value: ' . $value . '<br />';
			}
		} //foreach end 
		if ($returnval == 0) {
			$results[] = $idarray;
			$results[] = $error;
		} else {
			$results[] = $pids;
			$results[] = $error;
		}
		return $results;
	}
}

if (isset ($_POST['selection'])) {

	$link = new linkChecker;
	$fromCache = false;
	$idArray = $link->processRange($_POST['selection'], 'id', 0);

	foreach ($idArray[0] as $page => $id) {
		$pageUrl = rtrim($modx->config['site_url'],$modx->config['base_url']) . $modx->makeUrl($id);

		// Print the url were checking
		print "<strong>Checking link: <a href=\"" . $pageUrl . "\">" . $pageUrl . "</a> ...</strong><br />\n";
		ob_flush();
		flush();

		// Check it
		$html = $link->getEverything($pageUrl);
		if (!$html) {
			print "Sorry that link appears not to exist.<br /><br />\n";
		} else {

			// Get status code
			$done = $link->getStatusCode($html, $pageUrl);

			// Print results
			print "" . $done . "<br /><br />\n";

			// Get urls from main page

			$urls = $link->setUniqueUrls($html, $pageUrl);
			for ($i = 0; isset ($urls[$i]); $i++) {

				// Print the link were checking
				print "Checking link: <a href=\"" . $urls[$i] . "\">" . $urls[$i] . "</a> ...<br />\n";
				ob_flush();
				flush();
				if (array_key_exists($urls[$i], $link->linkCache) && $link->linkCache[$urls[$i]] != 0) {
					$done = $link->linkCache[$urls[$i]] . ' (from cache)';
					$fromCache = true;
				} else {
					// Get everything
					$html = $link->getHead($urls[$i]);

					// Check it
					if (!$html) {
						$done = "Sorry that link appears not to exist.<br /><br />\n";
					} else {
						// Get status code
						$done = $link->getStatusCode($html, $urls[$i]);
					}
				}
				// Show the results
				print "" . $done . "<br /><br />\n";

				ob_flush();
				flush();

				// Sleep so we don't send requests to the same server to fast
				if (!$fromCache)
					sleep(3);
				else
					$fromCache = false;
			}
		}
	}
} else {

	print "PHP link checker to help get rid of any dead links on your web pages.<br />\n";
	print "To use this free online like checker,  Just type in the box below the page\n";
	print "uri you want to check then click the \"Check links\" button. Then the link\n";
	print "checker will crawl your web page and get the links out of it and check them.\n";
	print "It will return a list of links it has checked and tell you the status i.e.\n";
	print "404 NOT FOUND,  200 OK,  ect.</p>\n";
	print "<form method=\"post\">\n";
	print "<p><input type=\"text\" name=\"selection\" value=\"\" size=\"40\" />\n";
	print "<input type=\"submit\" value=\"Check links\" />\n";
	print "</p></form><p>\n";
}
